#include "singlecache_wrapper.H"
#include <iostream>
#include <fstream>

using namespace std;

int num_cores = 1;
int l2_num_banks = 1;

//vector<mem_request_t>	core_0_serviced_req_q;
int 	L1_size		= 32*1024;
int 	L2_size		= 1024*1024/4;

// (TODO)
int	L1_block_size	= 32;
int	L1_assoc	= 2;
int	L2_block_size	= 128;
int	L2_assoc	= 2;

int	L1_hit_latency	= 5;
int	L1_miss_latency	= 200;
int	L2_hit_latency	= 20;
int	L2_miss_latency	= 200;	

unsigned long long 	cpu_cycle;

void SingleCacheWrapper::init() {


	//============================================================
	// A. Instantiate Core & Cache
	//============================================================
       // Instantiate Modules
	L1       = new Cache(0, 1, 1, L1_size, L1_block_size, L1_assoc, L1_hit_latency, L1_miss_latency,  "Level 1", false);
#ifdef USE_L2
	L2       = new Cache(/*no meaning of core_id as L2 is shared*/726, 2, 1, L2_size, L2_block_size, L2_assoc, L2_hit_latency, L2_miss_latency, "Level 2", false);
#endif
	// Connect Core_0->L1
	Core_0.set_lower_level_request_q        ( L1->get_incoming_request_q() );
	L1->set_upper_level_serviced_q          ( Core_0.get_serviced_q() );
#ifdef USE_L2
	L1->set_lower_level_request_q           ( L2->get_incoming_request_q() );
	// Connect L1->L2
	L2->set_upper_level_serviced_q  ( L1->get_serviced_q() );
	L2->set_lower_level_request_q   ( NULL);
#else
	L1->set_lower_level_request_q           ( NULL );
#endif
	trace_cycle = 0;
	cpu_cycle = 0;
}


	//============================================================
	// B. Get trace and execute Core & Cycle
	//============================================================

void SingleCacheWrapper::cycle (unsigned long long cyc, addr_type trace_addr,  enum opcode is_STORE ) {

#ifdef _DEBUG_
	cout << string(5, ' ') << setw(20) << "Address :" << "0x" << hex << trace_addr << endl;
	cout << string(5, ' ') << setw(20) << "CPU Cycle :" << dec << trace_cycle << endl;
	printf("[MSRHU][Addr=%x(%d)][Cycle=%d][STORE=%d]\n", trace_addr, trace_addr, trace_cycle, is_STORE);
#endif
	trace_cycle = cyc;

	// Execute until trace's CYCLE equals Core-Cache's cycle
	while(cpu_cycle!= trace_cycle)
	{

#ifdef _DEBUG_
		printf("[Cpu_Cycle=%lld][TraceCycle=%lld]\n", cpu_cycle, trace_cycle);
		printf("\n\n-----------------------<Start> Cycle=%lld--------------------\n", cpu_cycle);
#endif
	  
		Core_0.advance_cycle();

#ifdef USE_L2
		L2->advance_cycle();
#endif
		L1->advance_cycle();
#ifdef _DEBUG_
		L1->print_queue_status();
		L1->print_cache_block_status();
#ifdef USE_L2
		L2->print_queue_status();
		L2->print_cache_block_status();
#endif
#endif

		cpu_cycle++;
#ifdef _DEBUG_
		printf("=====================End of cycle = %lld=================\n", cpu_cycle);
#endif
	}
	#ifdef _SANITY_
	assert(cpu_cycle==trace_cycle);
	#endif
	// If you're here, then trace_cycle==cpu_cycle => Insert mem-request-ops
	Core_0.insert_incoming_request(is_STORE, trace_addr);
}

void SingleCacheWrapper::print_stats(ofstream& of)
{
	Core_0.print_stats(of);
	L1->print_stats(of);
#ifdef USE_L2
	L2->print_stats();
#endif
}

void SingleCacheWrapper::finish() {
	// Execute whatever's left in 'incoming-Queue'
	while(Core_0.request_all_finished()==false)
	{
#ifdef _DEBUG_
		printf("[CPU-Cycle=%lld]\n", cpu_cycle);
#endif

		Core_0.advance_cycle();
#ifdef USE_L2
		L2->advance_cycle();
#endif
		L1->advance_cycle();

		cpu_cycle++;
	}



	printf("Current CPU-Cycle=%lld\n", cpu_cycle);
}

//{
//
//}
